/*
 * parlhash.c
 * KJ's simplified stream archiver (kjssar)
 * 
 * (C)Copyright 2013-2015 by Klaus-J. Wolf, kj (at) seismic (dot) de
 * This file is licensed under the General Public License Version 2.
 * http://www.gnu.org/licenses/gpl-2.0.html
 * 
 * This is a proof-of-concept implementation.
 * 
 */


#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

#include <sys/mman.h>

#include <mhash.h>

#include "prelim.h"
#include "dynmisc.h"
#include "parlhash.h"


#define BUFSIZE 0x1000000        /* 16 MB */

/* #define BUFSIZE 0x10000000  */      /* 256 MB */
/* #define BUFSIZE 0x40000000u */



#define HASHES SIZE(glob_hashids)


static hashid glob_hashids[] = {MHASH_MD5, MHASH_SHA256, MHASH_RIPEMD160, MHASH_WHIRLPOOL};

static uchar *glob_buf;
static int    glob_sz;
static MHASH  glob_hash[HASHES];


static void *hashround(void *parm)
{
    unsigned n = ((unsigned *)parm)[0];
    
    mhash(glob_hash[n], glob_buf, glob_sz);
    return NULL;
}


uchar *genhash_f(int fh, int fh2)
{
    int i,j,r,p,n;
    pthread_t thr[HASHES];
    unsigned parm[HASHES];
    int totalsz;
    uchar *res;
    uchar *dp;
    
    glob_buf = malloc(BUFSIZE);
    MEMASSERT(glob_buf,NULL)
    
    for(i=0; i<HASHES; i++)
    {
        glob_hash[i] = mhash_init(glob_hashids[i]);
        parm[i] = i;
    }
    
    for(;;)
    {
        glob_sz = read(fh, glob_buf, BUFSIZE);
        if(glob_sz<0)
            IOASSERT(-1,NULL)
        if(glob_sz==0)
            break;
        if(fh2>=0)
        {
            r = write(fh2, glob_buf, glob_sz);
            if(r!=glob_sz)
                return NULL;   /* this is not necessarily an I/O error condition,
                                  but clearly not everything has been written */
        }
        for(i=0; i<HASHES; i++)
        {
            r = pthread_create(&thr[i], NULL, &hashround, (void *)&parm[i]);
            IOASSERT(r,NULL)
        }
        for(i=0; i<HASHES; i++)
        {
            pthread_join(thr[i], NULL);
        }
    }
    
    free(glob_buf);
    
    totalsz = 0;
    for(i=0; i<HASHES; i++)
    {
        totalsz += mhash_get_block_size(glob_hashids[i])+1;
    }
    
    res = malloc(totalsz);
    MEMASSERT(res,NULL)
    
    p=0;
    for(i=0; i<HASHES; i++)
    {
        /* dp = mhash_end_m(glob_hash[i], malloc); */
        dp = (uchar *)mhash_end(glob_hash[i]);
        n = mhash_get_block_size(glob_hashids[i]);
        for(j=0; j<n; j++)
            tohex(res+p+j*2, dp[j]);
        p+=n*2;
        if(i<HASHES-1)
            res[p++] = '-';
        else
            res[p++] = 0;
        /* free(dp); */
    }
    return res;
}


uchar *genhash_mmap_f(int fh, uint64 off, uint64 flen)
{
    int i,j,r,p,n;
    pthread_t thr[HASHES];
    unsigned parm[HASHES];
    int totalsz;
    uchar *res;
    uchar *dp;
    
    glob_buf = mmap(NULL, flen, PROT_READ, MAP_PRIVATE, fh, off);
    IOXASSERT(glob_buf,(void*)-1,NULL)
    
    for(i=0; i<HASHES; i++)
    {
        glob_hash[i] = mhash_init(glob_hashids[i]);
        parm[i] = i;
    }
    
    glob_sz = flen;
    
    for(i=0; i<HASHES; i++)
    {
        r = pthread_create(&thr[i], NULL, &hashround, (void *)&parm[i]);
        IOASSERT(r,NULL)
    }
    for(i=0; i<HASHES; i++)
    {
        pthread_join(thr[i], NULL);
    }
    
    munmap(glob_buf, flen);
    
    totalsz = 0;
    for(i=0; i<HASHES; i++)
    {
        totalsz += mhash_get_block_size(glob_hashids[i])+1;
    }
    
    res = malloc(totalsz);
    MEMASSERT(res,NULL)
    
    p=0;
    for(i=0; i<HASHES; i++)
    {
        /* dp = mhash_end_m(glob_hash[i], malloc); */
        dp = (uchar *)mhash_end(glob_hash[i]);
        n = mhash_get_block_size(glob_hashids[i]);
        for(j=0; j<n; j++)
            tohex(res+p+j*2, dp[j]);
        p+=n*2;
        if(i<HASHES-1)
            res[p++] = '-';
        else
            res[p++] = 0;
        /* free(dp); */
    }
    return res;
}


#ifdef MAIN

#include <stdio.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    int i, fh;
    uint64 n;
    int mmapflg;

    mmapflg=(argc>1 && !strcmp(argv[1],"-m"));
    
    for(i=(mmapflg?2:1); i<argc; i++)
    {
        fputs(argv[i], stdout);
        putchar('\t');
        fh = open(argv[i], O_RDONLY);
        if(mmapflg)
        {
            n=lseek(fh, 0, SEEK_END);
            lseek(fh, 0, SEEK_SET);
            
            puts(genhash_mmap(fh,0,n));
        }
        else
        {
            puts(genhash(fh));
        }
        fflush(stdout);
        close(fh);
    }
    return 0;
}

#endif
